From b0f2cd02ad39f3078c26c0dcf187bb5709167f3a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 21 Apr 2020 18:14:40 -0400 Subject: [PATCH] window: Tweak visible focus behavior Only turn on visible focus when a key event actually leads to a change in focus location (ie, 'keynav'). Make the visible focus disappear after 5 seconds of no keyboard interaction, to avoid permanent focus ring distraction. As an extra bonus, make it so that we make the focus visible while the Alt key is pressed. This gives us a 'find my focus!' shortcut, and goes well with the prexisting use of Alt for finding mnemonics. Discussed in: #2644 --- gtk/gtkwindow.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 9d87e68d35..194d0ef0c4 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -191,12 +191,15 @@ typedef struct guint mnemonics_display_timeout_id; + guint focus_visible_timeout; + gint scale; gint title_height; GtkWidget *title_box; GtkWidget *titlebar; GtkWidget *popup_menu; + GtkWidget *key_press_focus; GdkMonitor *initial_fullscreen_monitor; guint edge_constraints; @@ -4068,6 +4071,12 @@ gtk_window_finalize (GObject *object) priv->mnemonics_display_timeout_id = 0; } + if (priv->focus_visible_timeout) + { + g_source_remove (priv->focus_visible_timeout); + priv->focus_visible_timeout = 0; + } + g_clear_object (&priv->constraint_solver); g_clear_object (&priv->renderer); g_clear_object (&priv->resize_cursor); @@ -5362,6 +5371,36 @@ update_mnemonics_visible (GtkWindow *window, } } +static void +update_focus_visible (GtkWindow *window, + guint keyval, + GdkModifierType state, + gboolean visible) +{ + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); + + if (visible) + { + if (priv->focus_visible) + priv->key_press_focus = NULL; + else + priv->key_press_focus = priv->focus_widget; + + if ((keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R) && + ((state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_ALT_MASK)) == 0)) + gtk_window_set_focus_visible (window, TRUE); + } + else + { + if (priv->key_press_focus == priv->focus_widget) + gtk_window_set_focus_visible (window, FALSE); + else + gtk_window_set_focus_visible (window, TRUE); + + priv->key_press_focus = NULL; + } +} + static gboolean gtk_window_key_pressed (GtkWidget *widget, guint keyval, @@ -5371,8 +5410,7 @@ gtk_window_key_pressed (GtkWidget *widget, { GtkWindow *window = GTK_WINDOW (widget); - gtk_window_set_focus_visible (window, TRUE); - + update_focus_visible (window, keyval, state, TRUE); update_mnemonics_visible (window, keyval, state, TRUE); return FALSE; @@ -5387,6 +5425,7 @@ gtk_window_key_released (GtkWidget *widget, { GtkWindow *window = GTK_WINDOW (widget); + update_focus_visible (window, keyval, state, FALSE); update_mnemonics_visible (window, keyval, state, FALSE); return FALSE; @@ -7406,6 +7445,19 @@ gtk_window_get_focus_visible (GtkWindow *window) return priv->focus_visible; } +static gboolean +unset_focus_visible (gpointer data) +{ + GtkWindow *window = data; + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); + + priv->focus_visible_timeout = 0; + + gtk_window_set_focus_visible (window, FALSE); + + return G_SOURCE_REMOVE; +} + /** * gtk_window_set_focus_visible: * @window: a #GtkWindow @@ -7417,15 +7469,39 @@ void gtk_window_set_focus_visible (GtkWindow *window, gboolean setting) { + gboolean changed; + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); g_return_if_fail (GTK_IS_WINDOW (window)); - setting = setting != FALSE; + changed = priv->focus_visible != setting; - if (priv->focus_visible != setting) + priv->focus_visible = setting; + + if (priv->focus_visible_timeout) + { + g_source_remove (priv->focus_visible_timeout); + priv->focus_visible_timeout = 0; + } + + if (priv->focus_visible) + priv->focus_visible_timeout = g_timeout_add_seconds (5, unset_focus_visible, window); + + if (changed) { - priv->focus_visible = setting; + if (priv->focus_widget) + { + GtkWidget *widget; + + for (widget = priv->focus_widget; widget; widget = gtk_widget_get_parent (widget)) + { + if (priv->focus_visible) + gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_FOCUS_VISIBLE, FALSE); + else + gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_FOCUS_VISIBLE); + } + } g_object_notify_by_pspec (G_OBJECT (window), window_props[PROP_FOCUS_VISIBLE]); } } -- 2.30.2